package com.smart.car.record.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.smart.car.common.res.bean.MessageInfo;
import com.smart.car.common.res.bean.ResponseResult;
import com.smart.car.common.res.constant.Constants;
import com.smart.car.common.res.entity.FinanceBill;
import com.smart.car.common.res.exception.BusinessException;
import com.smart.car.record.client.BillFeignClient;
import com.smart.car.record.client.MessageClient;
import com.smart.car.record.entity.RecordChargeRule;
import com.smart.car.record.entity.RecordEntrance;
import com.smart.car.record.entity.RecordEntranceExample;
import com.smart.car.record.entity.RecordExport;
import com.smart.car.record.mapper.RecordEntranceMapper;
import com.smart.car.record.mapper.RecordExportMapper;
import com.smart.car.record.service.RecordExportService;
import com.smart.car.record.service.RedisService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;

/**
 * 车辆出库实现
 */
@Service
@Slf4j
public class RecordExportServiceImpl implements RecordExportService {

    @Autowired
    private RecordExportMapper ExistsMapper;

    @Autowired
    private RedisService redisService;

    /**
     * 计算停车的费用
     *
     * @param stayMintues 停车时长（分钟）
     * @return fee 停车费用
     */
    private float caluateFee(long stayMintues) {
        //获取redis中的收费规则
        String ruleStr = (String) redisService.getkey(Constants.cache.CHARGINGRULE);
        JSONArray array = JSONObject.parseArray(ruleStr);
        List<RecordChargeRule> rules = JSONObject.parseArray(array.toJSONString(), RecordChargeRule.class);
        float fee = 0;
        for (RecordChargeRule chargingRule : rules) {
            if (chargingRule.getStart() <= stayMintues && chargingRule.getEnds() > stayMintues) {
                fee = chargingRule.getFee();
                break;
            }
        }
        log.info("当前车辆出库，计算后所需支付的费用为：>>>>>>>>>>>>>>>>>>>>>>>>>>>" + fee);
        return fee;
    }


    @Autowired
    private RecordEntranceMapper entranceMapper;

    @Autowired
    private BillFeignClient billFeignClient;

    @Autowired
    private MessageClient messageClient;

    @Autowired
    private Source source;

    /**
     * 分布式事务演示-车辆离开业务
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @GlobalTransactional(rollbackFor = Exception.class)
    public int createExsits(String json) throws BusinessException {
        log.info("出库的车辆信息>>>>>>>>>>>>>>>>>>>>>>>> " + json);
        RecordExport exists = JSONObject.parseObject(json, RecordExport.class);
        int rtn = ExistsMapper.insertSelective(exists);
        log.info("新增该车辆的出库记录！>>>>>>>>>>>>>>>>>>>>>>>");

        // 停车费计算-对比车牌号，取最新记录
        RecordEntranceExample entranceExample = new RecordEntranceExample();
        entranceExample.setOrderByClause("create_date desc limit 0,1");
        entranceExample.createCriteria().andPlateNumEqualTo(exists.getPlateNum());
        List<RecordEntrance> entrances = entranceMapper.selectByExample(entranceExample);
        RecordEntrance lastEntrance = null;
        if (ObjectUtil.isNotEmpty(entrances)) {
            lastEntrance = entrances.get(0);
        }
        if (ObjectUtil.isNull(lastEntrance)) {
            throw new BusinessException("异常车辆，未找到该车牌的入场记录：" + exists.getPlateNum());
        }
        // 计算停车时长——时间轴上与此日期相同的点(日期带T和Z格式)
        Instant entryTime = lastEntrance.getCreateDate().toInstant();
        Duration duration = Duration.between(LocalDateTime.ofInstant(entryTime, ZoneId.systemDefault()),
                LocalDateTime.now());
        // 返回持续时间中的分钟数
        long mintues = duration.toMinutes();
        // 计算停车费用
        float fee = caluateFee(mintues);

        // 调用第三方支付服务来支付费用
        FinanceBill billing = new FinanceBill()
                .setFee(fee)
                .setDuration(Float.valueOf(mintues))
                .setPlateNum(exists.getPlateNum());
        // 存储支付记录
        ResponseResult<Integer> createRtn = billFeignClient.create(JSONObject.toJSONString(billing));
        if (createRtn.getData() > 0) {
            log.info("新增支付记录成功!");
        } else {
            throw new BusinessException("支付异常，启动finance服务的fallback回退策略...");
        }

        // 记录事件，模拟调用电子显示屏更新状态
        redisService.increase(Constants.cache.currentAviableStallAmt);
        log.info("更新停车场可用的兑换券>>> " + redisService.getkey(Constants.cache.currentAviableStallAmt));
        // 发送支付信息通知用户
        MessageInfo message = new MessageInfo();
        message.setMcontent("这是一条待付款信息");
        message.setMtype(Constants.msgtype.payMsg);
        this.source.output().send(MessageBuilder.withPayload(JSONObject.toJSONString(message)).build());
        log.info("生产消息给rocketmq, message服务以消费者的身份推送消息给用户...");

        // 记录生产过的消息
        ResponseResult<Integer> msgRtn = messageClient.sendNotice(JSONObject.toJSONString(message));
        if (msgRtn.getData() > 0) {
            log.info("插入car_message的消息记录成功!");
        } else {
            throw new BusinessException("发送异常，调用 message 服务fallback回滚策略 ...");
        }
        return rtn;
    }


    public static void main(String[] args) {
        Instant entryTime = DateUtil.yesterday().toInstant();
        //使用Instant时间和系统默认时区ID来创建LocalDateTime实例和当前日期时间求持续时间，是[)区间
        Duration duration = Duration.between(LocalDateTime.ofInstant(entryTime, ZoneId.systemDefault()),
                LocalDateTime.now());
        //返回持续时间中的分钟数
        long mintues = duration.toMinutes();
        System.out.println(entryTime);
        System.out.println(duration);
        System.out.println(mintues);
    }

}

